﻿using System;
using System.Windows.Controls;
using Microsoft.Phone.Shell;
using WindowsPhoneEssentials.Diagnostics;
using WindowsPhoneMVC.Navigation;
using WindowsPhoneMVC.Navigation.Interfaces;
using WindowsPhoneMVC.Phone.Interfaces;

namespace WindowsPhoneMVC.ActionResults
{
    /// <summary>
    /// View Result is base class for action results displaying views
    /// </summary>
    public abstract class ViewResult : ActionResult, IHaveNavigationCompletedSteps, IDisposable
    {
        private bool disposed;

        /// <summary>
        /// Initializes a new instance of the <see cref="ViewResult"/> class.
        /// </summary>
        /// <param name="viewName">Name of displayed view.</param>
        /// <param name="model">(View) model.</param>
        /// <param name="context">Navigation ontext.</param>
        protected ViewResult(string viewName, object model, NavigationContext context)
            : base(context)
        {
            ViewName = viewName;
            Model = model;
        }

        /// <summary>
        /// Gets name of the view.
        /// </summary>
        /// <value>The of the view.</value>
        public string ViewName { get; private set; }

        /// <summary>
        /// Gets (view)model.
        /// </summary>
        /// <value>The (view)model.</value>
        public object Model { get; private set; }

        /// <summary>
        /// Executes result.
        /// </summary>
        protected override void ExecuteInternal()
        {
            var viewModel = Model as ViewModelBase;
            if (viewModel != null)
            {
                viewModel.Navigator = Context.Navigator;
                viewModel.TaskManager = Context.TaskManager;
                viewModel.PageState = Context.Request.PageState;
                viewModel.ApplicationState = Context.PhoenixApplication.ApplicationTransientState;

                Context.PhoenixApplication.Activated += viewModel.Activated;
                Context.PhoenixApplication.Deactivated += viewModel.Deactivated;

                if (Model is IObscuredAware)
                {
                    var obsuredAware = (IObscuredAware) Model;
                    Context.PhoenixApplication.Obscured += obsuredAware.Obscured;
                    Context.PhoenixApplication.Unobscured += obsuredAware.Unobscured;
                }
            }

            WindowsPhoneEssentials.Threading.Execute.OnUIThread(
                () =>
                    {
                        var view = Context.ViewLocator.Locate(Context.Request.ControllerName, ViewName);
                        view.DataContext = Model;
                        view.Name = ViewName;
                        RenderView(Context.Navigator.NavigationFrame, view);
                    });
        }

        /// <summary>
        /// Renders view.
        /// </summary>
        /// <param name="navigationFrame">Navigation frame.</param>
        /// <param name="view">View.</param>
        protected abstract void RenderView(IMvcFrame navigationFrame, UserControl view);

        public virtual void Dispose()
        {
            if (disposed)
                throw new ObjectDisposedException("ViewResult");
            disposed = true;

            using (TraceScope.Create(this, TraceLevel.Debug, () => string.Format("Cleaning up ViewModel {0}", Model.GetType().Name)))
            {
                var viewModel = Model as ViewModelBase;
                if (viewModel != null)
                {
                    //Deactivate view model, the event will not be fired during normal navigation
                    viewModel.Deactivated(this, new DeactivatedEventArgs());
                    Context.PhoenixApplication.Activated -= viewModel.Activated;
                    Context.PhoenixApplication.Deactivated -= viewModel.Deactivated;
                }

                if (Model is IObscuredAware)
                {
                    var obsuredAware = (IObscuredAware)Model;
                    Context.PhoenixApplication.Obscured -= obsuredAware.Obscured;
                    Context.PhoenixApplication.Unobscured -= obsuredAware.Unobscured;
                }

                if (Model is IDisposable)
                    ((IDisposable)Model).Dispose();
            }
        }

        public void NavigationComplete()
        {
            //Activate view model, the event will not be fired during normal navigation
            var viewModel = Model as ViewModelBase;
            if (viewModel != null) 
                viewModel.Activated(this, new ActivatedEventArgs());
        }
    }
}
